#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

#include "../utils/command_analyse.h"
#include "../utils/command_parse.h"
#include "../utils/directory.h"
#include "../utils/msg.h"
#include "../utils/password.h"
#include "../utils/trans_file.h"
#include "log.h"
#include "task_queue.h"
#include "thread_pool.h"
#include "worker.h"

void cleanup_mutex(void *arg);
void *thread_func(void *);
void worker_event(const int netfd);

int make_worker(thread_pool_t *thread_pool) {
  printf("make_worker func\n");
  printf("thread_pool->thread_num = %d\n", thread_pool->thread_num);
  for (int i = 0; i < thread_pool->thread_num; ++i) {
    printf("%d create\n", i);
    pthread_create(&thread_pool->tid_arr[i], NULL, thread_func, thread_pool);
    printf("create thread %ld\n", thread_pool->tid_arr[i]);
  }
  return 0;
}

void cleanup_mutex(void *arg) {
  thread_pool_t *pthread_pool = (thread_pool_t *)arg;
  printf("unlock!\n");
  pthread_mutex_unlock(&pthread_pool->task_queue.mutex);
}

void *thread_func(void *arg) {
  printf("thread_func\n");
  thread_pool_t *pthread_pool = (thread_pool_t *)arg;
  while (1) {
    int netfd;
    pthread_mutex_lock(&pthread_pool->task_queue.mutex);

    // exit?
    if (pthread_pool->exit_flag == 1) {
      printf("I am going to die!\n");
      pthread_mutex_unlock(&pthread_pool->task_queue.mutex);
      pthread_exit(NULL);
    }

    // queue empty?
    while (task_queue_empty(&pthread_pool->task_queue)) {
      if (pthread_pool->exit_flag == 1) {
        printf("I am going to die!\n");
        pthread_mutex_unlock(&pthread_pool->task_queue.mutex);
        pthread_exit(NULL);
      }
      pthread_cond_wait(&pthread_pool->task_queue.cond,
                        &pthread_pool->task_queue.mutex);
    }

    netfd = pthread_pool->task_queue.front->netfd;
    // dequeue
    task_dequeue(&pthread_pool->task_queue);

    // unlock
    pthread_mutex_unlock(&pthread_pool->task_queue.mutex);

    // TODO: task
    /* send_file(netfd, "file1"); */
    worker_event(netfd);

    close(netfd);

    printf("child thread finished task!\n");
  }
  return NULL;
}
void worker_event(const int netfd) {
  char username[512] = {0};
  char user_passwd[512] = {0};
  char user_encrypted_passwd[512] = {0};
  char password[512] = {0};

  int ret = recv_msg(netfd, username);
  if (ret == 0) {
    return;
  }
  printf("username = %s\n", username);

  while (1) {
    bzero(user_passwd, sizeof(user_passwd));
    int ret = recv_msg(netfd, user_passwd);
    printf("user_passwd ret = %d\n", ret);
    if (ret == 0) {
      return;
    }
    printf("user_passwd = %s\n", user_passwd);

    password_encrypt(username, user_passwd, user_encrypted_passwd);
    get_encrypted_password(username, password);
    if (strcmp(user_encrypted_passwd, password) != 0) {
      send_msg(netfd, "Sorry try again!\n");
    } else {
      send_msg(netfd, "welcome!");
      break;
    }
  }

  LOG_LOGIN(username);

  char command[1024] = {0};
  char output[4096] = {0};
  char cwd[1024] = {0};
  char path[512] = {0};
  char log_msg[512] = {0};
  pwd(cwd);
  while (1) {
    bzero(command, sizeof(command));
    bzero(output, sizeof(output));

    int ret = recv_msg(netfd, command);
    printf("command ret = %d\n", ret);
    if (ret == 0) {
      return;
    }

    int argc;
    char *argv[16];

    command_parse(command, &argc, argv);

    if (argc == 0) {
      send_msg(netfd, "\n");
      continue;
    }

    int command_type = command_analyse(argv[0]);
    if (command_valid(command_type, argc) == 0) {
      send_msg(netfd, "arguments error!\n");
      continue;
    }

    bzero(path, sizeof(path));
    bzero(log_msg, sizeof(log_msg));
    switch (command_type) {
    case COMMAND_CD:
      sprintf(log_msg, "cd %s", argv[1]);
      LOG_FUNC(username, "cd", log_msg);
      ret = cd(argv[1], cwd, output);
      send_msg(netfd, output);
      if (ret == 0) {
        strcpy(cwd, output);
      }
      break;
    case COMMAND_LS:
      sprintf(log_msg, "ls %s", argc == 1 ? "current directory" : argv[1]);
      LOG_FUNC(username, "ls", log_msg);
      if (argc == 1) {
        ls(cwd, output);
      } else if (argc == 2) {
        ls(argv[1], output);
      }
      send_msg(netfd, output);
      break;
    case COMMAND_PWD:
      sprintf(log_msg, "pwd");
      LOG_FUNC(username, "pwd", log_msg);
      send_msg(netfd, cwd);
      break;
    case COMMAND_MKDIR:
      sprintf(log_msg, "mkdir %s", argv[1]);
      LOG_FUNC(username, "mkdir", log_msg);
      sprintf(path, "%s%s%s", cwd, "/", argv[1]);
      create_directory(path, 0666, output);
      send_msg(netfd, output);
      break;
    case COMMAND_RMDIR:
      sprintf(log_msg, "rmdir %s", argv[1]);
      LOG_FUNC(username, "rmdir", log_msg);
      sprintf(path, "%s%s%s", cwd, "/", argv[1]);
      remove_directory(path, output);
      send_msg(netfd, output);
      break;
    case COMMAND_RM:
      sprintf(log_msg, "rm %s", argv[1]);
      LOG_FUNC(username, "rm", log_msg);
      sprintf(path, "%s%s%s", cwd, "/", argv[1]);
      remove_file(path, output);
      send_msg(netfd, output);
      break;
    case COMMAND_GETS:
      sprintf(log_msg, "download %s", argv[1]);
      LOG_FUNC(username, "gets", log_msg);
      sprintf(path, "%s%s%s", cwd, "/", argv[1]);
      send_file(netfd, argv[1], path);
      break;
    case COMMAND_PUTS:
      sprintf(log_msg, "upload %s", argv[1]);
      LOG_FUNC(username, "puts", log_msg);
      recv_file(netfd, argv[1]);
      break;
    }
  }
}
